{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "61691b99-95ab-4268-92c9-faa2340b34cf",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-01T08:14:15.343178Z",
     "iopub.status.busy": "2024-05-01T08:14:15.342868Z",
     "iopub.status.idle": "2024-05-01T08:14:15.346648Z",
     "shell.execute_reply": "2024-05-01T08:14:15.346169Z",
     "shell.execute_reply.started": "2024-05-01T08:14:15.343145Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import scipy.fft as fft\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "from tqdm import tqdm\n",
    "import pandas as pd\n",
    "import pywt\n",
    "import imageio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "ff52fe68-47a3-4662-9b74-af78f2d26800",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-01T08:14:32.167096Z",
     "iopub.status.busy": "2024-05-01T08:14:32.166724Z",
     "iopub.status.idle": "2024-05-01T08:14:32.220222Z",
     "shell.execute_reply": "2024-05-01T08:14:32.219704Z",
     "shell.execute_reply.started": "2024-05-01T08:14:32.167062Z"
    }
   },
   "outputs": [],
   "source": [
    "def image_hash(img_path):\n",
    "    img = processing(img_path)\n",
    "    C_r_list = image_feature(img)\n",
    "    h_i = gen_hashing(C_r_list)\n",
    "    return h_i\n",
    "\n",
    "def processing(img_path):\n",
    "    \"\"\"\n",
    "    input：图片的路径\n",
    "    output：处理后的RGB图片\n",
    "    \"\"\"\n",
    "    try:\n",
    "        img = cv2.imread(img_path)\n",
    "        x = img.shape[0]//2 # 高度\n",
    "        y = img.shape[1]//2 # 宽度\n",
    "        Min = x if x<y else y\n",
    "        cropped_image = img[x-Min:x+Min, y-Min:y+Min] # 裁剪图像\n",
    "        img = cv2.resize((cropped_image), (512,512), interpolation=cv2.INTER_LINEAR)\n",
    "    except:\n",
    "        img = imageio.mimread(img_path)\n",
    "        img = np.array(img)\n",
    "        img = img[0]\n",
    "        img = img[:, :, 0:3]\n",
    "        x = img.shape[0]//2 # 高度\n",
    "        y = img.shape[1]//2 # 宽度\n",
    "        Min = x if x<y else y\n",
    "        cropped_image = img[x-Min:x+Min, y-Min:y+Min, :] # 裁剪图像\n",
    "        img = cv2.resize((cropped_image), (512,512), interpolation=cv2.INTER_LINEAR)\n",
    "#     out = cv2.GaussianBlur(img, (3, 3),1.3) # 使用python自带的高斯滤波\n",
    "    kernel = np.array([[1,2,1],[2,4,2],[1,2,1]])/16\n",
    "    out = cv2.filter2D(img, -1 , kernel=kernel)  # 二维滤波器\n",
    "    out = cv2.cvtColor(out, cv2.COLOR_BGR2RGB)\n",
    "    return out\n",
    "\n",
    "def image_feature(img):\n",
    "    \"\"\"\n",
    "    iamge:(512,512,3)\n",
    "    return: array格式(x,64,64)\n",
    "    \"\"\"\n",
    "    # 计算参考颜色\n",
    "    refer_color = np.array([np.mean(img[:,:,0]), np.mean(img[:,:,1]), np.mean(img[:,:,2])])\n",
    "    A = gene_A(refer_color, img)\n",
    "    M = np.zeros((A.shape[0]//32, A.shape[0]//32))\n",
    "    for index_i, i in enumerate(range(0, 512, 32)):\n",
    "        for index_j, j in enumerate(range(0, 512, 32)):\n",
    "            M[index_i][index_j] = np.mean(A[i:i+32,j:j+32])\n",
    "    coeffs2 = pywt.WaveletPacket2D(data=M, wavelet='haar', mode='symmetric')\n",
    "    return coeffs2[\"a\"].data\n",
    "\n",
    "def gene_A(refer_color, img):\n",
    "    \"\"\"\n",
    "    refer_color: 参考颜色\n",
    "    return: 颜色向量矩阵\n",
    "    \"\"\"\n",
    "    A = np.zeros((img.shape[0], img.shape[1]))\n",
    "    refer_sum = np.dot(refer_color, refer_color)\n",
    "    for i in range(A.shape[0]):\n",
    "        for j in range(A.shape[1]):\n",
    "            pixel = np.array(img[i, j, :])\n",
    "            # 计算像素的颜色向量角\n",
    "            dot_product = np.dot(pixel, refer_color)\n",
    "            norm_product = np.linalg.norm(pixel) * np.linalg.norm(refer_color)+1e-20\n",
    "            angle = np.arccos(dot_product / norm_product)\n",
    "            sin_angle = np.sin(angle)\n",
    "            A[i, j] = sin_angle\n",
    "    return A\n",
    "\n",
    "def gen_hashing(feature_matrix):\n",
    "    \"\"\"\n",
    "    生成图像哈希值,先把特征矩阵转置再flatten，达到按照列拼接的目的\n",
    "    input:array (x,64,64)\n",
    "    output:list (x)\n",
    "    \"\"\"\n",
    "    h = feature_matrix.T.flatten()\n",
    "    return h * 10000 + 0.5\n",
    "\n",
    "def dist_img(h1,h2):\n",
    "    return np.sqrt(sum((h1 - h2) ** 2))\n",
    "\n",
    "def dis_different_dir(path, des_path):\n",
    "    # 目录下图片之间的距离\n",
    "    dirs = os.listdir(path)\n",
    "    image_set_list = []\n",
    "    image_hashing_value_set = []\n",
    "    for i in tqdm(dirs,ncols = 50):\n",
    "    # for i in dirs:\n",
    "        image_hashing_value_set.append(image_hash(os.path.join(path, i)))\n",
    "    for i in range(len(image_hashing_value_set)):\n",
    "        for j in range(i+1,len(image_hashing_value_set)):\n",
    "            image_set_list.append(dist_img(image_hashing_value_set[i],image_hashing_value_set[j]))\n",
    "    for i in range(int(np.ceil(len(image_set_list)/1000000))-1):\n",
    "        start = i*1000000\n",
    "        end = (i+1)*1000000\n",
    "        pd.DataFrame(image_set_list[start:end]).to_excel(os.path.join(des_path,f\"different_image_result_{i+1}.xlsx\"),index = False)\n",
    "    pd.DataFrame(image_set_list[end+1:]).to_excel(os.path.join(des_path,f\"different_image_result_{i+2}.xlsx\"),index = False)\n",
    "    return image_set_list,image_hashing_value_set\n",
    "\n",
    "def dis_similar_dir(path):\n",
    "    \"\"\"\n",
    "    path:相同图片每种操作的目录\n",
    "    n:环数\n",
    "    block_size:分区大小\n",
    "    return:所有相同图片哈希距离的列表\n",
    "    \"\"\"\n",
    "    # 计算相同类型图片的距离\n",
    "    dir_rotation = os.listdir(path)\n",
    "    dis_similar = []\n",
    "    for i in dir_rotation:\n",
    "        if (os.path.splitext(i)[1] == \".bmp\") | (os.path.splitext(i)[1] == \".jpg\"):\n",
    "            h1 = image_hash(os.path.join(path, i))\n",
    "            dir_temp = os.path.join(path,os.path.splitext(i)[0])\n",
    "            for j in os.listdir(dir_temp):\n",
    "                h2 = image_hash(os.path.join(dir_temp,j))\n",
    "                dis_similar.append(dist_img(h1,h2))\n",
    "    return dis_similar\n",
    "\n",
    "def dir_dis_similar_dir(total_path,des_path):\n",
    "    \"\"\"\n",
    "    total_path:相同图片各种操作的根目录\n",
    "    des_path:存放结果的目录\n",
    "    return：存放每一种操作相同图片哈希距离的字典\n",
    "    \"\"\"\n",
    "    fold_each_attack = os.listdir(total_path)\n",
    "    dir_each_attack= {}\n",
    "    if not os.path.exists(os.path.join(des_path,\"similar_image_result\")):\n",
    "        os.makedirs(os.path.join(des_path,\"similar_image_result\"))\n",
    "    # total_path = os.path.join(total_path,\"similar_image_result\")\n",
    "    for i in tqdm(fold_each_attack,ncols = 50):\n",
    "        attack_path = os.path.join(total_path,i)\n",
    "        dir_each_attack[i] = dis_similar_dir(attack_path)\n",
    "        file_name = i + \".xlsx\"\n",
    "        pd.DataFrame(dir_each_attack[i]).to_excel(os.path.join(des_path,\"similar_image_result\",file_name),index = False)\n",
    "    return dir_each_attack\n",
    "\n",
    "def my_auc(image_similar, image_different):\n",
    "    from scipy import integrate\n",
    "    sum1 = len(image_similar)\n",
    "    sum2 = len(image_different)\n",
    "    tpr = []\n",
    "    fpr = []\n",
    "    threshold_max = (max(max(image_similar), max(image_different)))\n",
    "    for threshold in np.linspace(0,threshold_max,50):\n",
    "        tpr_number = len(image_similar[image_similar <= threshold])\n",
    "        fpr_number = len(image_different[image_different <= threshold])\n",
    "        tpr.append(tpr_number/sum1)\n",
    "        fpr.append(fpr_number/sum2)\n",
    "    return integrate.trapezoid(tpr,fpr)\n",
    "\n",
    "def stat_analysis(result_path):\n",
    "    \"\"\"\n",
    "    result_path:保存结果的路径\n",
    "    \"\"\"\n",
    "    dirs = os.listdir(result_path)\n",
    "    for i in dirs:\n",
    "        file_path = os.path.join(result_path, i)\n",
    "        if os.path.isdir (file_path):\n",
    "            for j in os.listdir(file_path):\n",
    "                xlsx_path = os.path.join(file_path,j)\n",
    "                data = pd.read_excel(xlsx_path,header = 0)[0]\n",
    "                print(f\"{j}的最小值：{min(data)}最大值：{max(data)}平均值：{np.mean(data)}\")\n",
    "        else:\n",
    "            xlsx_path = file_path\n",
    "            data = pd.read_excel(xlsx_path,header = 0)[0]\n",
    "            print(f\"{i}的最小值：{min(data)}最大值：{max(data)}平均值：{np.mean(data)}\")\n",
    "\n",
    "def auc_analysis(dir_path):\n",
    "    \"\"\"\n",
    "    dir_path:保存结果的路径\n",
    "    \"\"\"\n",
    "    similar_image_result_path = os.path.join(dir_path,\"similar_image_result\")\n",
    "    data_different =pd.Series()\n",
    "    for i in os.listdir(dir_path):\n",
    "        if not os.path.isdir(os.path.join(dir_path,i)):\n",
    "            print(os.path.join(dir_path,i))\n",
    "            temp = pd.read_excel(os.path.join(dir_path,i),header = 0)[0]\n",
    "            data_different = pd.concat((data_different,temp),axis=0)\n",
    "    similar_image_result_path_total = pd.Series()\n",
    "    for i in os.listdir(similar_image_result_path):\n",
    "        data_similar = pd.read_excel(os.path.join(similar_image_result_path,i),header = 0)[0]\n",
    "        similar_image_result_path_total= pd.concat((similar_image_result_path_total,data_similar),axis=0)\n",
    "        print(f\"{i}和不同图片的auc值：{my_auc(data_similar,data_different)}\")\n",
    "    print(f\"所有相同图片和不同图片 的auc值：{my_auc(similar_image_result_path_total,data_different)}\")\n",
    "\n",
    "def copy_dectection_result(query_path, test_path):\n",
    "    import glob\n",
    "    query_hash = []\n",
    "    test_hash = [] # 后 160 的结果是复制图像的结果\n",
    "    dist_set = []\n",
    "    for i in tqdm(os.listdir(query_path), ncols = 50):\n",
    "        query_hash.append(image_hash(os.path.join(query_path, i)))\n",
    "    for i in tqdm(os.listdir(os.path.join(test_path, \"different_dataset\")), ncols =50):\n",
    "        test_hash.append(image_hash(os.path.join(test_path, \"different_dataset\", i)))\n",
    "    for root, dirs, files in os.walk(os.path.join(test_path, \"copy_attack\")):\n",
    "        for file in tqdm(glob.glob(os.path.join(root, '*.[a-zA-Z]*')), ncols = 50):  # 你可以根据需要修改文件类型，例如 *.png, *.gif 等 \n",
    "            test_hash.append(image_hash(file))\n",
    "    print(len(test_hash), len(query_hash))\n",
    "    print(\"开始计算哈希距离\")\n",
    "    for i in tqdm(test_hash, ncols = 50):\n",
    "        for j in query_hash:\n",
    "            dist_set.append(dist_img(i, j))\n",
    "    pd.DataFrame(dist_set).to_excel(\"./复制检测结果/复制检测距离结果（1160）.xlsx\", index = False)\n",
    "    \n",
    "def P_R_result(path):\n",
    "    dist_set = np.array(pd.read_excel(path))\n",
    "    min_dist = min(dist_set)\n",
    "    max_dist = max(dist_set)\n",
    "    P = []\n",
    "    R = []\n",
    "    for threshold in np.linspace(min_dist, max_dist, 50):\n",
    "        diff_res = [1 if np.all(dist_set[i:i+10] > threshold) else 0 for i in np.arange(0, 9900, 10)]\n",
    "        copy_res = [1 if np.any(dist_set[i:i+10] <= threshold) else 0 for i in np.arange(9900, 11600, 10)]\n",
    "        # diff_res = [1 if np.all(dist_set[i:i+10] > threshold) else 0 for i in np.arange(0, 10000, 10)]\n",
    "        # copy_res = [1 if np.any(dist_set[i:i+10] <= threshold) else 0 for i in np.arange(10000, 11600, 10)]\n",
    "        TP = sum(copy_res)\n",
    "        # FN = 160 - TP\n",
    "        FN = 170 - TP\n",
    "        FP = 990 - sum(diff_res)\n",
    "        P.append(TP / (TP + FP))\n",
    "        R.append(TP / (TP + FN))\n",
    "    return np.linspace(min_dist, max_dist, 50), P, R\n",
    "\n",
    "def copy_dectection_result(dataset_path):\n",
    "    query_path = os.path.join(dataset_path, \"query_dataset\")\n",
    "    attack_path = os.path.join(dataset_path, \"test_dataset\", \"copy_attack\")\n",
    "    different_path = os.path.join(dataset_path, \"test_dataset\", \"different_dataset\")\n",
    "    result = pd.DataFrame()\n",
    "    for i in tqdm(os.listdir(query_path), ncols = 50):\n",
    "        temp = []\n",
    "        h1 = image_hash(os.path.join(query_path, i))\n",
    "        img_attack_path = os.path.join(attack_path, i.split(\".\")[0])\n",
    "        for j in os.listdir(img_attack_path):\n",
    "            h2 = image_hash(os.path.join(img_attack_path, j))\n",
    "            temp.append(dist_img(h1, h2))\n",
    "        for j in os.listdir(different_path):\n",
    "            h2 = image_hash(os.path.join(different_path, j))\n",
    "            temp.append(dist_img(h1, h2))\n",
    "        result[i] = temp\n",
    "    result.to_excel(\"./复制检测结果/第二次/result_DWT.xlsx\", index = False)\n",
    "    return result\n",
    "\n",
    "def cal_mAP(result_path):\n",
    "    result = pd.read_excel(result_path)\n",
    "    mAP = 0\n",
    "    for i in result:\n",
    "        now_10 = result[i][:10]\n",
    "        sorted_list = list(result[i])\n",
    "        sorted_list.sort()\n",
    "        max_10 = sorted_list[:10] # 这里是从小到大进行排序\n",
    "        mAP += cal_ap(now_10, max_10)\n",
    "    return mAP / 16\n",
    "\n",
    "def cal_ap(now_10, max_10):\n",
    "    AP = 0\n",
    "    bool_list = []\n",
    "    for i in now_10:\n",
    "        if i in max_10:\n",
    "            bool_list.append(1)\n",
    "        else:\n",
    "            bool_list.append(0)\n",
    "    for index, i in enumerate(bool_list):\n",
    "        AP += i * (sum(bool_list[:index+1]) / (index + 1))\n",
    "    return AP / len(now_10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6620a8b3-2992-44a5-952e-40be4324b59f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-01T08:14:33.236502Z",
     "iopub.status.busy": "2024-05-01T08:14:33.236048Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 12%|▉      | 2/16 [1:43:01<11:59:57, 3085.56s/it]"
     ]
    }
   ],
   "source": [
    "result = copy_dectection_result(\"../new_data/copy_detection_dataset_2\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4f4fe860-39e4-41b3-aa41-1273f91819cf",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-02T10:52:19.693175Z",
     "iopub.status.busy": "2024-05-02T10:52:19.692852Z",
     "iopub.status.idle": "2024-05-02T10:52:20.602683Z",
     "shell.execute_reply": "2024-05-02T10:52:20.602157Z",
     "shell.execute_reply.started": "2024-05-02T10:52:19.693151Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.047068452380952384"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cal_mAP(\"./复制检测结果/第二次/result_DWT.xlsx\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c2c14ec-0bee-42ea-996c-6de970c85b8e",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2024-05-01T08:14:09.330159Z",
     "iopub.status.idle": "2024-05-01T08:14:09.331458Z",
     "shell.execute_reply": "2024-05-01T08:14:09.331080Z",
     "shell.execute_reply.started": "2024-05-01T08:14:09.331043Z"
    }
   },
   "outputs": [],
   "source": [
    "# copy_dectection_result(\"../new_data/copy_detection_dataset/query_dataset/\", \"../new_data/copy_detection_dataset/test_dataset/\")\n",
    "theshold, P, R = P_R_result(\"./复制检测结果/复制检测距离结果（1160）.xlsx\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c50ef06-d44f-4d98-a1de-8a9e88a21cf4",
   "metadata": {
    "execution": {
     "iopub.status.busy": "2024-05-01T08:14:09.333630Z",
     "iopub.status.idle": "2024-05-01T08:14:09.334969Z",
     "shell.execute_reply": "2024-05-01T08:14:09.334610Z",
     "shell.execute_reply.started": "2024-05-01T08:14:09.334571Z"
    }
   },
   "outputs": [],
   "source": [
    "print(R)\n",
    "print(P)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "a9467496-801a-49a8-a597-382fedff7bce",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-04-21T01:34:40.691991Z",
     "iopub.status.busy": "2024-04-21T01:34:40.689718Z",
     "iopub.status.idle": "2024-04-21T01:34:40.909859Z",
     "shell.execute_reply": "2024-04-21T01:34:40.908830Z",
     "shell.execute_reply.started": "2024-04-21T01:34:40.691897Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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1qh///5yOBwaM2aMduzY0e75q1ev1g033KBu3bopKytLixYtUm1tbYcKBgC0b0fJOZVWtv0z1pBUWlmrHSXnglcU0A6/w8gbb7yhnJwc5ebmateuXbrllls0efJknT7tffvh119/XYsXL1Zubq727dunl19+WW+88YZ+8pOfdLp4AEBrp6t8+8eer+cBgeZ3GFm1apXmzZunuXPnaujQoVq3bp3i4+O1YcMGr+d//PHHuv322zVz5kz1799fkyZN0v3333/VuykAgI5JSXB06XlAoPm1mqa+vl47d+7UkiVLPMesVqsmTJigoqIir9eMHTtWGzdu1I4dOzR69GgdOXJEW7Zs0axZs9p8n7q6OtXVXd450OlsWifvcrnkcrn8KRlX0dyetKt56ANzRWL735qZoLREu8qddfI2K8QiKS3JrlszE0Lic0diH4STQLa/r6/pVxipqKhQY2OjUlNTWxxPTU3V/v37vV4zc+ZMVVRU6I477pBhGGpoaNCjjz7a7jDNihUrtHz58lbHt27dqvj4eH9Kho/y8/PNLiHq0QfmirT2n5Zm0QZn881vyxXfMWRImppao/fy/mpCZW2LtD4IN4Fo/5qaGp/OC/g+I4WFhXr++ef1wgsvaMyYMTp06JAef/xxPfPMM1q6dKnXa5YsWaKcnBzP106nU1lZWZo0aZISExMDXXJUcblcys/P18SJExUbG2t2OVGJPjBXpLb/NEkjvijXs1v2q8x5+U5zepJDT00drMk3prZ9cZBFah+Ei0C2f/PIxtX4FUaSk5Nls9lUXl7e4nh5ebnS0tK8XrN06VLNmjVLDz/8sCTppptuUnV1tR555BE99dRTslpbT1ux2+2y2+2tjsfGxvIHNUBoW/PRB+aKxPb/h+GZmnpzhtYWHtIvtx7UwOTuys+5Szar5eoXmyAS+yCcBKL9fX09vyawxsXFaeTIkSooKPAcc7vdKigoUHZ2ttdrampqWgUOm80mSTIM1rgDQCDZrBaNH9x0F+T8RVfIBhFEN7+HaXJycjRnzhyNGjVKo0eP1urVq1VdXa25c+dKkmbPnq2MjAytWLFCkjR9+nStWrVKt956q2eYZunSpZo+fbonlAAAAiejVzdJ0rnqetXUNyg+jieBILT4/SdyxowZOnPmjJYtW6aysjINHz5ceXl5nkmtx48fb3En5Kc//aksFot++tOf6uTJk+rbt6+mT5+u5557rus+BQCgTUndYpXoiJGztkEnv76ob6UmmF0S0EKH4vHChQu1cOFCr98rLCxs+QYxMcrNzVVubm5H3goA0AUyesXLWerUV+cJIwg9PJsGAKJA5qWhmq++vmhyJUBrhBEAiAIZPZvDiG/7PgDBRBgBgCjQfGfkJHdGEIIIIwAQBTJ7Ne1ezTANQhFhBACiAHNGEMoIIwAQBZrDSMWFOtW6Gk2uBmiJMAIAUSCpW6x62Jt2czh5nrsjCC2EEQCIAhaLxbOihkmsCDWEEQCIEswbQagijABAlMjoxV4jCE2EEQCIEp69RpgzghBDGAGAKMFeIwhVhBEAiBJMYEWoIowAQJRoHqYpr6pVXQN7jSB0EEYAIEr07h4nR6xVhiGVnq81uxzAgzACAFHCYrF45o0wiRWhhDACAFEkk+W9CEGEEQCIIkxiRSgijABAFGF5L0IRYQQAokgGW8IjBBFGACCKsAsrQhFhBACiSHMYKa28KFej2+RqgCaEEQCIIsnd7YqLscptSGWV7DWC0EAYAYAoYrValNmTeSMILYQRAIgyGew1ghBDGAGAKMMkVoQawggARBn2GkGoIYwAQJTJ6MkwDUILYQQAogzDNAg1hBEAiDLNE1hLz9eq0W2YXA1AGAGAqJOS4FCszaIGt6FyJ3uNwHyEEQCIMjarRf3YawQhhDACAFGISawIJYQRAIhCnkms3BlBCCCMAEAUYq8RhBLCCABEIc8wzXmGaWA+wggARCGGaRBKCCMAEIWa9xo5db5WbvYagckIIwAQhdISHbJZLapvdOvMhTqzy0GUI4wAQBSKsVmVnuSQxPJemI8wAgBRKoONzxAiCCMAEKVY3otQQRgBgCjVPImVMAKzEUYAIEp5lveeJ4zAXIQRAIhSmb14Pg1CA2EEAKJUZs+mOSMnv74ow2CvEZiHMAIAUSotySGrRaprcKviQr3Z5SCKEUYAIErFxViVmsheIzAfYQQAolgmK2oQAggjABDFmvcaYUUNzEQYAYAodnkXVoZpYB7CCABEMc9eIwzTwESEEQCIYuzCilBAGAGAKHbl82nYawRmIYwAQBTr17Npae9FV6O+rnGZXA2iFWEEAKKYPcamlAS7JCaxwjyEEQCIckxihdkIIwAQ5TKumDcCmIEwAgBRjqf3wmyEEQCIcp5hGnZhhUkIIwAQ5S7vwkoYgTkIIwAQ5dhrBGYjjABAlGseprlQ1yDnxQaTq0E0IowAQJRzxNqU3CNOknSCSawwAWEEAOBZ3sskVpiBMAIAUCaTWGGiDoWRNWvWqH///nI4HBozZox27NjR7vnnz5/XggULlJ6eLrvdruuvv15btmzpUMEAgK7HXiMwU4y/F7zxxhvKycnRunXrNGbMGK1evVqTJ0/WgQMHlJKS0ur8+vp6TZw4USkpKXrzzTeVkZGhY8eOqWfPnl1RPwCgC7AlPMzkdxhZtWqV5s2bp7lz50qS1q1bp82bN2vDhg1avHhxq/M3bNigc+fO6eOPP1ZsbKwkqX///p2rGgDQpTJ6MUwD8/gVRurr67Vz504tWbLEc8xqtWrChAkqKiryes0777yj7OxsLViwQG+//bb69u2rmTNn6sknn5TNZvN6TV1dnerq6jxfO51OSZLL5ZLLxSOuu1Jze9Ku5qEPzEX7N0m7tJrm5PmaoLcFfWCuQLa/r6/pVxipqKhQY2OjUlNTWxxPTU3V/v37vV5z5MgRbdu2TQ888IC2bNmiQ4cO6bHHHpPL5VJubq7Xa1asWKHly5e3Or5161bFx8f7UzJ8lJ+fb3YJUY8+MFe0t39doyTFqPJig/7fO1vUze/75p0X7X1gtkC0f02Nb3OQAv7Hze12KyUlRS+++KJsNptGjhypkydP6he/+EWbYWTJkiXKycnxfO10OpWVlaVJkyYpMTEx0CVHFZfLpfz8fE2cONEzjIbgog/MRftftmLPB/q6xqUbR9+pwWkJQXtf+sBcgWz/5pGNq/ErjCQnJ8tms6m8vLzF8fLycqWlpXm9Jj09XbGxsS2GZIYMGaKysjLV19crLi6u1TV2u112u73V8djYWP6gBghtaz76wFy0f9O28F/XVKq8yqWbsoLfFvSBuQLR/r6+nl9Le+Pi4jRy5EgVFBR4jrndbhUUFCg7O9vrNbfffrsOHTokt9vtOXbw4EGlp6d7DSIAAHNcfmAey3sRXH7vM5KTk6P169fr1Vdf1b59+zR//nxVV1d7VtfMnj27xQTX+fPn69y5c3r88cd18OBBbd68Wc8//7wWLFjQdZ8CANBpmayogUn8njMyY8YMnTlzRsuWLVNZWZmGDx+uvLw8z6TW48ePy2q9nHGysrL03nvvadGiRbr55puVkZGhxx9/XE8++WTXfQoAQKc1L+9lS3gEW4cmsC5cuFALFy70+r3CwsJWx7Kzs/XJJ5905K0AAEGSeen5NNwZQbDxbBoAgKQrdmHlzgiCjDACAJB0eZjmXHW9qusaTK4G0YQwAgCQJCU6YpXoaBq95+4IgokwAgDwyLg0b4QH5iGYCCMAAI/Ly3vZawTBQxgBAHh4wgjDNAgiwggAwOPyLqyEEQQPYQQA4MFeIzADYQQA4OHZa4QwgiAijAAAPJrDSMWFOtW6Gk2uBtGCMAIA8EjqFqsedvYaQXARRgAAHhaLhUmsCDrCCACgBfYaQbARRgAALWQwiRVBRhgBALRw+c4IYQTBQRgBALRwea8RhmkQHIQRAEALzRNYWU2DYCGMAABaaB6mKXfWqa6BvUYQeIQRAEALvbvHqVusTZJUer7W5GoQDQgjAIAWLBaLZ0UNk1gRDIQRAEAr7DWCYCKMAABaYRIrgokwAgBo5fLyXsIIAo8wAgBoJZNdWBFEhBEAQCsZzBlBEBFGAACtNN8ZKXPWytXoNrkaRDrCCACgleTudsXFWOU2pLJK9hpBYBFGAACtWK0WZV5aUXOCoRoEGGEEAOAVG58hWAgjAACvWFGDYCGMAAC8Yq8RBAthBADg1eVdWJkzgsAijAAAvMpkzgiChDACAPCqeZimtLJWDew1ggAijAAAvEpJsCvWZlGj21B5VZ3Z5SCCEUYAAF5ZrRb1uzRv5KtzzBtB4BBGAABtujyJlXkjCBzCCACgTUxiRTAQRgAAbbq81wjDNAgcwggAoE0M0yAYCCMAgDYxTINgIIwAANrU/LC8U+cvyu02TK4GkYowAgBoU1qiQzarRa5GQ6fZawQBQhgBALQpxmZVepJDEpNYETiEEQBAu5jEikAjjAAA2nV5eS9hBIFBGAEAtKtfz6Zhmu2Hzqjo8Fk1MpEVXSzG7AIAAKErb0+pfld0TJJUdPicig5/ovQkh3KnD9WUYekmV4dIwZ0RAIBXeXtKNX/jLlVedLU4XlZZq/kbdylvT6lJlSHSEEYAAK00ug0tf3evvA3INB9b/u5ehmzQJQgjAIBWdpScU2llbZvfNySVVtZqR8m54BWFiEUYAQC0crqq7SDSkfOA9hBGAACtpCQ4uvQ8oD2EEQBAK6MH9FZ6kkOWNr5vkZSe5NDoAb2DWRYiFGEEANCKzWpR7vShktRmIMmdPlQ2a1vfBXxHGAEAeDVlWLrWPjhCaUkth2Li42xa++AI9hlBl2HTMwBAm6YMS9fEoWnaUXJOH+wv14sflahnt1hNvjHN7NIQQbgzAgBol81qUfagPlo08QbZY6w6VVmrA+VVZpeFCEIYAQD4pFucTXdclyxJKth32uRqEEkIIwAAn40fkiJJKthXbnIliCSEEQCAz8YPbgoju0+c19kLdSZXg0hBGAEA+Cw9qZuGpifKMKTCA2fMLgcRgjACAPDLdy8N1Wzbz7wRdA3CCADAL81DNR8ePKP6BrfJ1SASEEYAAH65JbOnknvEqaquQf9xlKf2ovMIIwAAv1itFo27oXlVDUM16DzCCADAb83zRgr2l8swDJOrQbjrUBhZs2aN+vfvL4fDoTFjxmjHjh0+Xbdp0yZZLBbdd999HXlbAECIuONbfRVrs+jY2Rodqag2uxyEOb/DyBtvvKGcnBzl5uZq165duuWWWzR58mSdPt3+rbqjR4/qRz/6ke68884OFwsACA097DG6bWAfSdI2hmrQSX6HkVWrVmnevHmaO3euhg4dqnXr1ik+Pl4bNmxo85rGxkY98MADWr58uQYOHNipggEAoaF5VU3BfnZjRef49dTe+vp67dy5U0uWLPEcs1qtmjBhgoqKitq87mc/+5lSUlL00EMP6aOPPrrq+9TV1amu7vLOfk6nU5Lkcrnkcrn8KRlX0dyetKt56ANz0f4d953rekuS/uPo1zrrrFFit9gOvQ59YK5Atr+vr+lXGKmoqFBjY6NSU1NbHE9NTdX+/fu9XrN9+3a9/PLLKi4u9vl9VqxYoeXLl7c6vnXrVsXHx/tTMnyUn59vdglRjz4wF+3fMWndbCq7KP3vf3tfI5I7N5GVPjBXINq/pqbGp/P8CiP+qqqq0qxZs7R+/XolJyf7fN2SJUuUk5Pj+drpdCorK0uTJk1SYmJiIEqNWi6XS/n5+Zo4caJiYzv2rxp0Dn1gLtq/c/bYDmr99qP6ulumpk27qUOvQR+YK5Dt3zyycTV+hZHk5GTZbDaVl7ccHywvL1daWlqr8w8fPqyjR49q+vTpnmNud9NufTExMTpw4IAGDRrU6jq73S673d7qeGxsLH9QA4S2NR99YC7av2Mm3piu9duP6sNDFbJYbYqxdXzHCPrAXIFof19fz68/NXFxcRo5cqQKCgo8x9xutwoKCpSdnd3q/MGDB+vzzz9XcXGx5797771X48aNU3FxsbKysvx5ewBAiBlxTU8ldYvV+RqXdp84b3Y5CFN+D9Pk5ORozpw5GjVqlEaPHq3Vq1erurpac+fOlSTNnj1bGRkZWrFihRwOh4YNG9bi+p49e0pSq+MAgPATY7Pq7hv66u3iUyrYd1rf7t/b7JIQhvwOIzNmzNCZM2e0bNkylZWVafjw4crLy/NMaj1+/LisVjZ2BYBo8d0hqXq7+JS27S/X4qmDzS4HYahDE1gXLlyohQsXev1eYWFhu9e+8sorHXlLAECIuutbfWWzWnSw/IJOnKtRVm9WPcI/3MIAAHRKUnysRl3bS5JUsI8N0OA/wggAoNMuPziPreHhP8IIAKDTxg9umjf46ZFzulDXYHI1CDeEEQBApw3q213X9olXfaNb27+sMLschBnCCACg0ywWi7576e7INh6cBz8RRgAAXaJ53si2/WfkdnfuOTWILoQRAECX+Hb/3uphj1HFhTp9frLS7HIQRggjAIAuERdj1Xeub3ooKkt84Q/CCACgyzSvqmGJL/xBGAEAdJm7b+gri0X64pRTZZW1ZpeDMEEYAQB0meQedg3P6ilJ2sbdEfiIMAIA6FIThrDEF/4hjAAAutT4wU1LfLcfqlCtq9HkahAOCCMAgC41OC1B/ZIcqnW5VXT4rNnlIAwQRgAAXcpisWj8pQ3Q3meJL3xAGAEAdLnLW8OflmGwGyvaRxgBAHS57EF95Ii1qrSyVvtKq8wuByGOMAIA6HKOWJvuuK6vJFbV4OoIIwCAgGh+cB67seJqCCMAgIAYd0NTGCk+cV4VF+pMrgahjDACAAiItCSHhmUkyjCkwgNnzC4HIYwwAgAIGM+D81jii3YQRgAAAfPdS7uxfnjwjOob3CZXg1BFGAEABMxNGUnqm2BXdX2jdpScM7schCjCCAAgYKxWi8bf0LyqhqEaeEcYAQAEVPPW8AX72I0V3hFGAAABdcd1yYqzWXX8XI0On6k2uxyEIMIIACCguttjdNugPpLYjRXeEUYAAAHXvKrm/X3sxorWCCMAgIAbfymM7Dz2tc7X1JtcDUINYQQAEHBZveN1Q2qCGt2G/naQ3VjREmEEABAUzatqtvHgPHwDYQQAEBTN80YKD5xRQyO7seIywggAIChuvaaXesbHqvKiS7uOnze7HIQQwggAIChsVovGXdqN9fdFR/V28UkVHT6rRjcboUW7GLMLAABEjz494iRJ7/69VO/+vVSSlJZo17Q0i6aZWRhMxZ0RAEBQ5O0p1csflbQ6Xu6s04aDVr33BRuiRSvCCAAg4Brdhpa/u1feBmSajz331/0M2UQpwggAIOB2lJxTaWVtO2dYVFpZpx0l54JWE0IHYQQAEHCnq9oLIv6fh8hCGAEABFxKgqNLz0NkIYwAAAJu9IDeSk9yyNLmGYbSk+waPaB3EKtCqCCMAAACzma1KHf6UK/faw4oT00dLJu17biCyEUYAQAExZRh6Vr74Aj1TbC3OJ6WZNc/X+/W5BtTTaoMZiOMAACCZsqwdL35aLYkKdZm0R/m3aYPcr6jW/qwpDeaEUYAAEHVPBRjs1qUPagPQzMgjAAAAHMRRgAAgKkIIwAAwFSEEQAAYCrCCAAAMBVhBAAAmIowAgAATEUYAQAApiKMAAAAUxFGAACAqQgjAADAVIQRAABgKsIIAAAwFWEEAACYijACAABMRRgBAACmIowAAABTEUYAAICpCCMAgKBqdBue/y06fNbzNaJXjNkFAACiR96eUi19+wtJkqvR0P3rP1Faol3T0iyaZnJtME+H7oysWbNG/fv3l8Ph0JgxY7Rjx442z12/fr3uvPNO9erVS7169dKECRPaPR8AEJny9pRq/sZdOlNV1+J4ubNOGw5a9d4X5SZVBrP5HUbeeOMN5eTkKDc3V7t27dItt9yiyZMn6/Tp017PLyws1P33368PPvhARUVFysrK0qRJk3Ty5MlOFw8ACA+NbkPL390rbwMyzcee++t+hmyilN9hZNWqVZo3b57mzp2roUOHat26dYqPj9eGDRu8nv/aa6/pscce0/DhwzV48GC99NJLcrvdKigo6HTxAIDwsKPknEora9s5w6LSyjrtKDkXtJoQOvyaM1JfX6+dO3dqyZIlnmNWq1UTJkxQUVGRT69RU1Mjl8ul3r17t3lOXV2d6uou38ZzOp2SJJfLJZfL5U/JuIrm9qRdzUMfmIv2D47S89U+n+dyJQa4GlwpkH8HfH1Nv8JIRUWFGhsblZqa2uJ4amqq9u/f79NrPPnkk+rXr58mTJjQ5jkrVqzQ8uXLWx3funWr4uPj/SkZPsrPzze7hKhHH5iL9g+sI5UWSbarn/dFsbZ8tTvwBaGVQPwdqKmp8em8oK6mWblypTZt2qTCwkI5HI42z1uyZIlycnI8XzudTs9ck8REEnNXcrlcys/P18SJExUbG2t2OVGJPjAX7R8cjW5Db/7qQ5U767zOG5EMpSTYtXDGXbJZLUGuLroF8u9A88jG1fgVRpKTk2Wz2VRe3nLGc3l5udLS0tq99pe//KVWrlyp999/XzfffHO759rtdtnt9lbHY2Nj+WERILSt+egDc9H+gRUr6el7b9T8jbtkkbwEEosssqiyzq3UxLb/sYrACcTfAV9fz68JrHFxcRo5cmSLyafNk1Gzs7PbvO7nP/+5nnnmGeXl5WnUqFH+vCUAIEJMGZautQ+OUFpSy7DRt0ec4mMMlVfVacb/LdKp8xdNqhBm8XuYJicnR3PmzNGoUaM0evRorV69WtXV1Zo7d64kafbs2crIyNCKFSskSf/yL/+iZcuW6fXXX1f//v1VVlYmSerRo4d69OjRhR8FABDqpgxL18ShadpRck6nq2qVkuDQrZkJev3Pf9WGkh46erZGM14s0usP36as3swRjBZ+h5EZM2bozJkzWrZsmcrKyjR8+HDl5eV5JrUeP35cVuvlGy5r165VfX29vve977V4ndzcXD399NOdqx4AEHZsVouyB/XxfO1yudTHIb320Lc1+5WdOna2Rv/04id6fd4YZfaKbxFcRg/ozZySCNShCawLFy7UwoULvX6vsLCwxddHjx7tyFsAAKJMv57d9MYj2Zr50ic6cqZa9/6f7YqzWXXmQr3nnPQkh3KnD9WUYekmVoquxoPyAAAhIy3JoU2P3Kb0RIcqLza0CCKSVFZZq/kbdylvT6lJFSIQCCMAgJDSp7tdjUZbC4CbLH93L1vHRxDCCAAgpDTNEalr8/uGpNLKWraOjyCEEQBASDld1d4zbPw/D6GPMAIACCkpCb5tepbcvfXmmAhPhBEAQEgZPaC30pMcutoC3v/1b//JRNYIQRgBAIQUm9Wi3OlDJandQFLuZGVNpCCMAABCTvPW8amJbQ/FsLImchBGAAAhacqwdP3q+8PbPYeVNZGBMAIACFkVF9pe4nslVtZ0TOHfy/WtpVv1eJFV31q6VYV/Lzeljg5tBw8AQDD4umKGlTXeNboNz7N9krvbJUtTwEtJcOj+9Z9ccWbTvYkfvP6Z9Lp0dOU9Qa2TMAIACF2+PhMvSp6dd2W4uNqDA/P2lGr5u3tVWun/XaP+izcHNZAQRgAAIcvXYRpfzwt17YUNb+GirQcH5u0p1fyNu9SZab2Ffy/X3TenduIVfEcYAQCELF+HX0476/R28cmr3i0w09XuarQXNiR5DRfNDw5c++AITyBpdBta/u7eTgURqWnI5ujNwbk7QhgBAIQuHzPFc1v2ef5/epJDS+8Zol7d7V7nSngLK96CQqPb0O+LjurYuRpd2ztes7L7Ky7G2u48jLaC0NXuarR1J6OsslaPbtylnvGxXsNF87En3vy7dh77Wmeq6nSw/EKHhmbMRBgBAISs007/f6mWVtbqsdd3t/n9bw5teAsK8XE2XXQ16sqHBz+3ZZ++OyRFe0462/xl723YpL2gMX/jLq2Zeaue2byv3bBxvsbV3keWs7ZB6z8qafecUEYYAQCErHPV9V3+mlcObUjehz9q6htbXec2pPy9p31+7SnD0tsdMmk+9vimYrm6YNO2u67vq7GD+shZ69KaDw53+vVemTmq06/hK8IIACBk9e7R9Ut2DTWN/jz9zheSLJ2eW/HN15aknD/+p/7ts690tKL6qkMmXRFEJOnRuwYpe1AfNboN/WnXSZVV1nbqswVr8qrEpmcAgBCWlujbE3z9ZUgqc9aprAPDQL6oqW9Uwf7TOlxRHZDXv5JFTcNDowf0luT7s33aE+x9RggjAICQ1fwE33D0/VGZ+tGk6306t3f3uDaDg0VSz/hYWdQ6XDR/nTt9aIuJs83P9klrp+3Skxx6bFzGpa/ckpqGZoIdRCSGaQAAIaz5X/nzN+6SpC4dUgm0/3JrpkYP6K3XPj3e5pCJRVJakkNL7xmqBa/vkkUtP2NzvFj5X2+SpFYTbdPa2GdEagokE4emXXXlz6LxN2rLli2aNm2KYmNju+rj+4UwAgAIac3/yv/mL+Ke8bE6X+Nq9QvcFxbp0hOBLSp3dm5uhbfXTku6/Mu+OUy1FTSaw8Raa+vP+M2wcWW48GVPFZvVouxBfbrw0wUGYQQAEPK++a/85l/E+XvL/N7yvPlX99P33ihJXoNCR3kbNmkrTH0zaLT1Ga8MG+ESLvxFGAEAhAVvv4i9/QL/urpez2xuO6B8MwR4Cwre9hmxWnTVfUbaGjbxJWi09RmjAWEEABDWvP0Cnzzs6nMlmrUVFLpyB9a26kQTwggAIOL4+4vf2/k2q0UP3Tmw06+Nq2NpLwAAMBVhBAAAmIowAgAATEUYAQAApiKMAAAAUxFGAACAqQgjAADAVIQRAABgKsIIAAAwVVjswGpcejiA0+k0uZLI43K5VFNTI6fTadqjo6MdfWAu2t989IG5Atn+zb+3DaP9xxCGRRipqqqSJGVlZZlcCQAA8FdVVZWSkpLa/L7FuFpcCQFut1unTp1SQkKCLBbvDyBCxzidTmVlZenEiRNKTEw0u5yoRB+Yi/Y3H31grkC2v2EYqqqqUr9+/WS1tj0zJCzujFitVmVmZppdRkRLTEzkh4DJ6ANz0f7mow/MFaj2b++OSDMmsAIAAFMRRgAAgKkII1HObrcrNzdXdrvd7FKiFn1gLtrffPSBuUKh/cNiAisAAIhc3BkBAACmIowAAABTEUYAAICpCCMAAMBUhJEosGbNGvXv318Oh0NjxozRjh072jx3/fr1uvPOO9WrVy/16tVLEyZMaPd8+MafPrjSpk2bZLFYdN999wW2wAjnb/ufP39eCxYsUHp6uux2u66//npt2bIlSNVGJn/7YPXq1brhhhvUrVs3ZWVladGiRaqtrQ1StZHlww8/1PTp09WvXz9ZLBb9+c9/vuo1hYWFGjFihOx2u6677jq98sorgS3SQETbtGmTERcXZ2zYsMH44osvjHnz5hk9e/Y0ysvLvZ4/c+ZMY82aNcbu3buNffv2GT/4wQ+MpKQk46uvvgpy5ZHD3z5oVlJSYmRkZBh33nmn8Y//+I/BKTYC+dv+dXV1xqhRo4xp06YZ27dvN0pKSozCwkKjuLg4yJVHDn/74LXXXjPsdrvx2muvGSUlJcZ7771npKenG4sWLQpy5ZFhy5YtxlNPPWX86U9/MiQZb731VrvnHzlyxIiPjzdycnKMvXv3Gr/97W8Nm81m5OXlBaxGwkiEGz16tLFgwQLP142NjUa/fv2MFStW+HR9Q0ODkZCQYLz66quBKjHidaQPGhoajLFjxxovvfSSMWfOHMJIJ/jb/mvXrjUGDhxo1NfXB6vEiOdvHyxYsMAYP358i2M5OTnG7bffHtA6o4EvYeSJJ54wbrzxxhbHZsyYYUyePDlgdTFME8Hq6+u1c+dOTZgwwXPMarVqwoQJKioq8uk1ampq5HK51Lt370CVGdE62gc/+9nPlJKSooceeigYZUasjrT/O++8o+zsbC1YsECpqakaNmyYnn/+eTU2Ngar7IjSkT4YO3asdu7c6RnKOXLkiLZs2aJp06YFpeZoV1RU1KK/JGny5Mk+/97oiLB4UB46pqKiQo2NjUpNTW1xPDU1Vfv37/fpNZ588kn169ev1R9M+KYjfbB9+3a9/PLLKi4uDkKFka0j7X/kyBFt27ZNDzzwgLZs2aJDhw7psccek8vlUm5ubjDKjigd6YOZM2eqoqJCd9xxhwzDUENDgx599FH95Cc/CUbJUa+srMxrfzmdTl28eFHdunXr8vfkzgjatHLlSm3atElvvfWWHA6H2eVEhaqqKs2aNUvr169XcnKy2eVEJbfbrZSUFL344osaOXKkZsyYoaeeekrr1q0zu7SoUVhYqOeff14vvPCCdu3apT/96U/avHmznnnmGbNLQ4BwZySCJScny2azqby8vMXx8vJypaWltXvtL3/5S61cuVLvv/++br755kCWGdH87YPDhw/r6NGjmj59uueY2+2WJMXExOjAgQMaNGhQYIuOIB35O5Cenq7Y2FjZbDbPsSFDhqisrEz19fWKi4sLaM2RpiN9sHTpUs2aNUsPP/ywJOmmm25SdXW1HnnkET311FOyWvl3dCClpaV57a/ExMSA3BWRuDMS0eLi4jRy5EgVFBR4jrndbhUUFCg7O7vN637+85/rmWeeUV5enkaNGhWMUiOWv30wePBgff755youLvb8d++992rcuHEqLi5WVlZWMMsPex35O3D77bfr0KFDnhAoSQcPHlR6ejpBpAM60gc1NTWtAkdzODR4nFrAZWdnt+gvScrPz2/390anBWxqLELCpk2bDLvdbrzyyivG3r17jUceecTo2bOnUVZWZhiGYcyaNctYvHix5/yVK1cacXFxxptvvmmUlpZ6/quqqjLrI4Q9f/vgm1hN0zn+tv/x48eNhIQEY+HChcaBAweMv/zlL0ZKSorx7LPPmvURwp6/fZCbm2skJCQYf/jDH4wjR44YW7duNQYNGmR8//vfN+sjhLWqqipj9+7dxu7duw1JxqpVq4zdu3cbx44dMwzDMBYvXmzMmjXLc37z0t4f//jHxr59+4w1a9awtBed99vf/ta45pprjLi4OGP06NHGJ5984vneXXfdZcyZM8fz9bXXXmtIavVfbm5u8AuPIP70wTcRRjrP3/b/+OOPjTFjxhh2u90YOHCg8dxzzxkNDQ1Brjqy+NMHLpfLePrpp41BgwYZDofDyMrKMh577DHj66+/Dn7hEeCDDz7w+nO9uc3nzJlj3HXXXa2uGT58uBEXF2cMHDjQ+Nd//deA1mgxDO55AQAA8zBnBAAAmIowAgAATEUYAQAApiKMAAAAUxFGAACAqQgjAADAVIQRAABgKsIIAAAwFWEEAACYijACAABMRRgBAACmIowAAABT/X92SJpVC40D9wAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(R, P,  \"-o\")\n",
    "plt.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "7078cd65-8a34-4958-8ddb-3fc37487ec1a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-04-20T12:48:13.944165Z",
     "iopub.status.busy": "2024-04-20T12:48:13.943597Z",
     "iopub.status.idle": "2024-04-20T12:48:18.579413Z",
     "shell.execute_reply": "2024-04-20T12:48:18.578482Z",
     "shell.execute_reply.started": "2024-04-20T12:48:13.944135Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "38557.163292764795"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "h1 = image_hash(\"../new_data/multiple_attack_2/IDR+AWGN+CA/kodim01/kodim01_10.jpg\")\n",
    "# h2 = image_hash(\"../data/mandrill512.tif\")\n",
    "h2 = image_hash(\"../new_data/multiple_attack_2/IDR+AWGN+CA/kodim01.jpg\")\n",
    "dist_img(h1,h2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c6381e7-d573-4f53-8d17-8e26a224c12a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-03-27T12:45:02.690037Z",
     "iopub.status.busy": "2024-03-27T12:45:02.688357Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 12%|▉       | 398/3307 [15:21<1:53:35,  2.34s/it]/tmp/ipykernel_1734883/2601718186.py:53: RuntimeWarning: invalid value encountered in scalar divide\n",
      "  cos_theta = np.dot(refer_color, temp_color) / (np.linalg.norm(refer_color) * np.linalg.norm(temp_color))\n",
      " 47%|███▎   | 1539/3307 [59:57<1:08:16,  2.32s/it]"
     ]
    }
   ],
   "source": [
    "dis_different_dir(\"../new_data/different_image/\"  ,\"./新数据集结果/纯四元数版本_环形分区结果\")\n",
    "dir_dis_similar_dir(\"../new_data/similar_image_5/\",\"./新数据集结果/纯四元数版本_环形分区结果\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "305f1dca-b9d4-46f5-9b04-9f899caa33bd",
   "metadata": {
    "collapsed": true,
    "execution": {
     "iopub.execute_input": "2024-03-28T02:26:05.080593Z",
     "iopub.status.busy": "2024-03-28T02:26:05.079261Z",
     "iopub.status.idle": "2024-03-28T02:29:07.766264Z",
     "shell.execute_reply": "2024-03-28T02:29:07.764857Z",
     "shell.execute_reply.started": "2024-03-28T02:26:05.080521Z"
    },
    "jupyter": {
     "outputs_hidden": true
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "brightness.xlsx的最小值：194.502458755603最大值：4113.160178103732平均值：1195.0097020629996\n",
      "contrast.xlsx的最小值：752.5591408834678最大值：6697.921795311656平均值：2594.95961906096\n",
      "gamma correction.xlsx的最小值：2008.924090311788最大值：32982.51467634807平均值：11403.676351112379\n",
      "gaussian.xlsx的最小值：4191.204352188975最大值：13262.29590783066平均值：9498.533120023312\n",
      "jpeg.xlsx的最小值：nan最大值：nan平均值：5765.036467945516\n",
      "rotation.xlsx的最小值：nan最大值：nan平均值：12765.30437417824\n",
      "salt_pepper.xlsx的最小值：4022.671632563962最大值：13262.29590783066平均值：9000.075352902717\n",
      "speckle.xlsx的最小值：4191.204352188975最大值：13262.29590783066平均值：9498.533120023312\n",
      "watermark.xlsx的最小值：0.0最大值：2497.425395038136平均值：491.04464345146204\n",
      "different_image_result_1.xlsx的最小值：2108.908948930398最大值：72006.88315802427平均值：18505.265928684985\n",
      "different_image_result_2.xlsx的最小值：312.4925757950107最大值：76749.66602539462平均值：17593.656930546007\n",
      "different_image_result_3.xlsx的最小值：2332.155611056346最大值：79658.44742324523平均值：19651.01755335988\n",
      "different_image_result_4.xlsx的最小值：2021.936204880441最大值：64973.1681215857平均值：17360.722200692057\n",
      "different_image_result_5.xlsx的最小值：1104.052281588892最大值：70863.37422437927平均值：18620.472128391895\n",
      "different_image_result_6.xlsx的最小值：1211.594069452827最大值：59790.6434115925平均值：16469.524232373493\n",
      "./新数据集结果/纯四元数版本_环形分区结果/different_image_result_1.xlsx\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1734883/2601718186.py:165: FutureWarning: The behavior of array concatenation with empty entries is deprecated. In a future version, this will no longer exclude empty items when determining the result dtype. To retain the old behavior, exclude the empty entries before the concat operation.\n",
      "  data_different = pd.concat((data_different,temp),axis=0)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./新数据集结果/纯四元数版本_环形分区结果/different_image_result_2.xlsx\n",
      "./新数据集结果/纯四元数版本_环形分区结果/different_image_result_3.xlsx\n",
      "./新数据集结果/纯四元数版本_环形分区结果/different_image_result_4.xlsx\n",
      "./新数据集结果/纯四元数版本_环形分区结果/different_image_result_5.xlsx\n",
      "./新数据集结果/纯四元数版本_环形分区结果/different_image_result_6.xlsx\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1734883/2601718186.py:169: FutureWarning: The behavior of array concatenation with empty entries is deprecated. In a future version, this will no longer exclude empty items when determining the result dtype. To retain the old behavior, exclude the empty entries before the concat operation.\n",
      "  similar_image_result_path_total= pd.concat((similar_image_result_path_total,data_similar),axis=0)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "brightness.xlsx和不同图片的auc值：0.8752685519631498\n",
      "contrast.xlsx和不同图片的auc值：0.8742684378584351\n",
      "gamma correction.xlsx和不同图片的auc值：0.6087730168341421\n",
      "gaussian.xlsx和不同图片的auc值：0.7537322188725082\n",
      "jpeg.xlsx和不同图片的auc值：0.0\n",
      "rotation.xlsx和不同图片的auc值：0.0\n",
      "salt_pepper.xlsx和不同图片的auc值：0.7725453867954193\n",
      "speckle.xlsx和不同图片的auc值：0.7537322188725082\n",
      "watermark.xlsx和不同图片的auc值：0.875309124306911\n",
      "所有相同图片和不同图片 的auc值：0.6359174968232971\n"
     ]
    }
   ],
   "source": [
    "stat_analysis(\"./新数据集结果/纯四元数版本_环形分区结果/\")\n",
    "auc_analysis(\"./新数据集结果/纯四元数版本_环形分区结果/\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "image",
   "language": "python",
   "name": "image"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
